//
// Created by alexander on 1/27/19.
//
#include <stdlib.h>
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
#include </home/alexander/CLionProjects/FFmpegLogTest/log/log.h>
#include </home/alexander/CLionProjects/FFmpegLogTest/util/util.h>
#include </home/alexander/CLionProjects/FFmpegLogTest/cutvideo.h>

void printPacketInfo(const AVFormatContext *avFormatContext, const AVPacket *avPacket, const char *tag)
{
    AVRational *time_base = &avFormatContext->streams[avPacket->stream_index]->time_base;

    printf("%s: pts:%s pts_time:%s dts:%s dts_time:%s duration:%s stream_index:%d\n",
           tag,
           av_ts2str(avPacket->pts), av_ts2timestr(avPacket->pts, time_base),
           av_ts2str(avPacket->dts), av_ts2timestr(avPacket->dts, time_base),
           av_ts2str(avPacket->duration), av_ts2timestr(avPacket->duration, time_base),
           avPacket->stream_index);
}

int cutVideo(double fromSeconds, double endSeconds, const char *inputFileName, const char *outputFileName)
{
    AVOutputFormat *avOutputFormat = NULL;
    AVFormatContext *inputAVFormatContext = NULL, *outputAVFormatContext = NULL;
    AVPacket avPacket;
    int returnCode, i;

    av_register_all();

    returnCode = avformat_open_input(&inputAVFormatContext, inputFileName, 0, 0);
    if(returnCode < 0)
    {
        logError("Could not open input file '%s'!", inputFileName);
        goto faile;
    }

    returnCode = avformat_find_stream_info(inputAVFormatContext, 0);
    if(returnCode < 0)
    {
        logError("Failed to retrieve input stream info.", "");
        goto faile;
    }

    av_dump_format(inputAVFormatContext, 0, inputFileName, 0);

    avformat_alloc_output_context2(&outputAVFormatContext, NULL, NULL, outputFileName);
    if(!outputAVFormatContext)
    {
        logError("Could not create output context!", "");
        returnCode = AVERROR_UNKNOWN;
        goto faile;
    }

    avOutputFormat = outputAVFormatContext->oformat;

    for (i = 0; i < inputAVFormatContext->nb_streams; i++)
    {
        AVStream *inputAVStream = inputAVFormatContext->streams[i];
        AVStream *outputAVStream = avformat_new_stream(outputAVFormatContext, inputAVStream->codec->codec);
        if(!outputAVStream)
        {
            logError("Failed allocating outout stream!\n", "");
            returnCode = AVERROR_UNKNOWN;
            goto faile;
        }

        returnCode = avcodec_copy_context(outputAVStream->codec, inputAVStream->codec);
        if(returnCode < 0)
        {
            logError("Failed to copy context from input to output stream codec context", "");
        }
        outputAVStream->codec->codec_tag = 0;
        if(outputAVFormatContext->oformat->flags & AVFMT_GLOBALHEADER)
        {
            outputAVStream->codec->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
        }
    }
    av_dump_format(outputAVFormatContext, 0, outputFileName, 1);

    if(!(avOutputFormat->flags & AVFMT_NOFILE))
    {
        returnCode = avio_open(&outputAVFormatContext->pb, outputFileName, AVIO_FLAG_WRITE);
        if(returnCode < 0)
        {
            logError("Could not open output file '%s'!", outputFileName);
            goto faile;
        }
    }

    returnCode = avformat_write_header(outputAVFormatContext, NULL);
    if(returnCode < 0)
    {
        logError("Error occurred when opening output file!\n", "");
        goto faile;
    }

    returnCode = av_seek_frame(inputAVFormatContext, -1, fromSeconds * AV_TIME_BASE, AVSEEK_FLAG_ANY);
    if(returnCode < 0)
    {
        logError("Error seek!\n", "");
        goto faile;
    }

    int64_t *dtsStartFrom = malloc(sizeof(int64_t) * inputAVFormatContext->nb_streams);
    memset(dtsStartFrom, 0, sizeof(int64_t) * inputAVFormatContext->nb_streams);
    int64_t  *ptsStartFrom = malloc(sizeof(int64_t) * inputAVFormatContext->nb_streams);
    memset(ptsStartFrom, 0, sizeof(int64_t) * inputAVFormatContext->nb_streams);

    while (1)
    {
        AVStream *inputStream, *outputStream;

        returnCode = av_read_frame(inputAVFormatContext, &avPacket);
        if(returnCode < 0)
        {
            logError("Read frame error!\n", "");
            break;
        }

        inputStream = inputAVFormatContext->streams[avPacket.stream_index];
        outputStream = outputAVFormatContext->streams[avPacket.stream_index];

        printPacketInfo(inputAVFormatContext, &avPacket, "in");

        /**判断数据是否已经读到该结束的地方*/
        if(av_q2d(inputStream->time_base) * avPacket.pts > endSeconds)
        {
            av_free_packet(&avPacket);
            break;
        }

        if(dtsStartFrom[avPacket.stream_index] == 0)
        {
            dtsStartFrom[avPacket.stream_index] = avPacket.dts;
            printf("dts_start_from: %s\n", av_ts2str(dtsStartFrom[avPacket.stream_index]));
        }

        if(ptsStartFrom[avPacket.stream_index] == 0)
        {
            ptsStartFrom[avPacket.stream_index] = avPacket.pts;
            printf("pts_start_from: %s\n", av_ts2str(ptsStartFrom[avPacket.stream_index]));
        }

        /**进行时间基的转换    拷贝数据*/
        avPacket.pts = av_rescale_q_rnd(avPacket.pts - ptsStartFrom[avPacket.stream_index], inputStream->time_base, outputStream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
        avPacket.dts = av_rescale_q_rnd(avPacket.dts - dtsStartFrom[avPacket.stream_index], inputStream->time_base, outputStream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);
        if(avPacket.pts < 0)
        {
            avPacket.pts = 0;
        }
        if(avPacket.dts < 0)
        {
            avPacket.dts = 0;
        }
        avPacket.duration = (int)av_rescale_q((int64_t)avPacket.duration, inputStream->time_base, outputStream->time_base);
        avPacket.pos = -1;
        printPacketInfo(outputAVFormatContext, &avPacket, "out");

        printf("pts->%d dts->%d\n\n", avPacket.pts, avPacket.dts);
        if(avPacket.pts < avPacket.dts)
        {
//            printf("抛弃异常帧！\n");
//            av_free_packet(&pkt);
//            //跳过异常帧
//            continue;
            avPacket.pts = avPacket.dts;
        }

        returnCode = av_interleaved_write_frame(outputAVFormatContext, &avPacket);
        if(returnCode < 0)
        {
            logError("Error muxing packet!\n", "");
            break;
        }
        av_packet_unref(&avPacket);
    }
    free(dtsStartFrom);
    free(ptsStartFrom);

faile:
    avformat_close_input(&inputAVFormatContext);

    /**close output*/
    if(outputAVFormatContext && !(avOutputFormat->flags & AVFMT_NOFILE))
    {
        avio_close(outputAVFormatContext->pb);
    }
    avformat_free_context(outputAVFormatContext);

    if(returnCode < 0 && returnCode != AVERROR_EOF)
    {
        logError("Error occurred: %s", av_err2str(returnCode));
        return -1;
    }

    return 0;
}
































